#ifndef __MINIUTIL_MULTINET_SOCKETMULTIPLEX_H__
#define __MINIUTIL_MULTINET_SOCKETMULTIPLEX_H__

#include <string>
#include <vector>
#include <map>
#include <deque>
#include "miniutil/buffer.h"
#include "miniutil/fifobuf.h"
#include "miniutil/thread.h"
#include "miniutil/os_compatible.h"

namespace miniutil { namespace multinet {

	const static int EVT_NONE = 0;
	const static int EVT_READ_READY =1;		//can read data from SocketMulplexService
	const static int EVT_WRITE_READY =2;	//can write data to SocketMulplexService
	const static int EVT_EXCEPTION = 4;
	const static int EVT_INCOMING = 8;
	const static int EVT_CONNECTED = 16;
	//const static int EVT_CLOSED = 32;

	struct MonSocketInstance
	{
	    static const int TCPBUF_SIZE=8192;
	    static const int UDPMSG_SIZE=1600;

		MonSocketInstance();
		int socevent;
		SOCKET soc;
		bool closeflag;
		//what invoker want
		int waitevt; 
	};

	struct MonTcpSocketInstance:public MonSocketInstance
	{
		MonTcpSocketInstance();

		miniutil::data::fifobuf recvbuf;
		int recvret;
		int sendret;

		//psendbuf :send function will set this pointer, so it must keep valid before EVT_WRITE_READY
		//sendlen :set by invoker
		//lenhavesent :actual data have sent to network layer
		//const char* psendbuf;
		//int sendlen;
		//int lenhavesent;
		std::deque<miniutil::data::shared_buffer> send_queue;

		void sendbuffer(char* pbuf, int len);

	};

	struct MonUdpSocketInstance:public MonSocketInstance
	{
		struct UDPMSG
		{
			UDPMSG();
			~UDPMSG();
			char* mpbuf;
			int mlen;
			sockaddr maddr;
			SOCKET soc;
		};

		std::vector<UDPMSG*> vsendbuf;
		std::vector<UDPMSG*> vrecvbuf;
	};

	class SocketMulplexService:public miniutil::runnable
	{
	public:
		//common interface
		SocketMulplexService();

		void start_service();
		void stop_service();
		bool is_readable(SOCKET soc);
		bool is_writeable(SOCKET soc);
		int del_socket(SOCKET soc);
	public:
        int check_tcp_connect(SOCKET soc);
        int check_tcp_listen(SOCKET soc);
        int check_tcp_read(SOCKET soc);

		//tcp interface
		//time out ? 
		bool is_incoming(SOCKET soc);   //check listen status
		bool is_exception(SOCKET soc);
		bool is_connected(SOCKET soc);  //check connec status
		bool is_closed(SOCKET soc); //check connected socket.

		int add_tcp_socket(SOCKET soc, int checkevt);
		int send(SOCKET s, const char* buf, int len);
		int recv(SOCKET s, char* buf, int len);
		SOCKET accept(SOCKET s, sockaddr* addr, int *addrlen);
	public:
		//udp interface
		int add_udp_socket(SOCKET soc, int checkevt);
		int sendto(SOCKET s, const char* buf,int len,const struct sockaddr* to);
		int recvfrom(SOCKET s, char *buf, int len, sockaddr *from);
		int limitnetspeed(int quota, int direction);
	protected:	
		std::map<SOCKET, MonTcpSocketInstance> malltcpsockets;
		std::map<SOCKET, MonUdpSocketInstance> malludpsockets;

		miniutil::mutex msockmtx;
		bool stopcmd;
		bool stopstus;
		virtual void run() = 0;

		int max_dquota;	//max download byte quota per second
		int max_uquota;	//max upload byte quota per second
		int left_dquota;
		int left_uquota;
		time_t mprechktime;


	};
	
	class SocketMulplexService_Select:public SocketMulplexService
	{
		public:
			virtual void run();
		protected:
			SOCKET init_fdset();
			void do_readable();
			void do_writable();
			void do_exception();
			fd_set writedfs;
			fd_set readdfs;
			fd_set excepdfs;
			std::vector<SOCKET> tcpchecksocs;
			std::vector<SOCKET> udpchecksocs;

	};

} }

#endif

